#if !defined(MOUSECTRACERMANGERIMPL5_H_INCLUDED)
#define MOUSECTRACERMANGERIMPL5_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef offsetof
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif	// #ifndef offsetof

#pragma warning(push)

#include <yvals.h>				// warning numbers get enabled in yvals.h

#pragma warning(disable: 4018)
#pragma warning(disable: 4100)
#pragma warning(disable: 4245)
#pragma warning(disable: 4512)
#pragma warning(disable: 4663)
#pragma warning(disable: 4710)
#pragma warning(disable: 4786)

#include <map>
#include <algorithm>

#include "MouseTracerManager.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// References
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ATL Under the Hood Part 4 By Zeeshan Amjad (http://www.codeproject.com/atl/atl_underthehood_4.asp)
// ATL Under the Hood Part 5 By Zeeshan Amjad (http://www.codeproject.com/atl/atl_underthehood_5.asp)
// "Under The Hood" in Feb 1998 issue of Microsoft System Journal by Matt Pietrek (http://www.microsoft.com/msj/0298/hood0298.aspx)
// "Under The Hood" in June 1998 issue of Microsoft System Journal by Matt Pietrek (http://www.microsoft.com/msj/0698/hood0698.aspx)
// Thunking WndProcs in ATL By Fritz Onion (http://www.pluralsight.com/fritz/Articles/cpprep0399.htm)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// forward reference;
template<class T>
class CMouseTracer;

/**
*	CMouseTracerManagerImpl5 class
*
*	Type 5 : setup application hook to monitor mouse activities, especially button down message,
*				to note the timiing at which CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl3> >-hook will be installed/removed dynamically on the active window
*
*/
class CMouseTracerManagerImpl5
{
	friend class CMouseTracerManager<CMouseTracerManagerImpl5>;
	class CMouseProcHook;
	friend class CMouseProcHook;

	class CMouseProcHook
	{
	private:
		class MouseProcThunk
		{
			//	mov	eax, dword ptr [esp+0Ch]			// 8B 44 24 0C
			//	mov	[12345678h], eax					// A3 78 56 34 12
			//	mov	dword ptr [esp+0Ch], [34567890h]	// C7 44 24 0C 90 78 56 34
			//	jmp [56789012h]							// E9 12 90 78 56
	public:

#if defined(_M_IX86)

#pragma pack(push, 1)
	struct _MouseProcThunk
	{
		DWORD	m_movEAXESP;	// mov eax, dword ptr [esp+0Ch] (esp+0Ch is lParam)
		BYTE	m_movEAX;		// mov [pThis->lparam], eax
		DWORD	m_lparam;		// [pThis->lParam]
		DWORD	m_movESPthis;	// mov dword ptr [esp+0Ch], [pThis]
		DWORD	m_this;			// [pThis]
		BYTE	m_jmp;			// jmp [MouseProc]
		DWORD	m_relproc;		// [Mouse Proc]
	};
#pragma pack(pop)

#else	// #if defined(_M_IX86)

#error Only X86 supported

#endif	// #if defined(_M_IX86)
		
		_MouseProcThunk thunk;
		
		void Init(HOOKPROC proc, void *pThis)
		{

#if defined(_M_IX86)

			thunk.m_movEAXESP	= 0x0C24448B;	// 8B 44 24 0C
			thunk.m_movEAX		= 0xA3;			// A3
			thunk.m_lparam		= (DWORD)CMouseProcHook::OffsetOfLPARAM(pThis);
			thunk.m_movESPthis	= 0x0C2444C7;	// C7 44 24 0C
			thunk.m_this		= (DWORD)pThis;
			thunk.m_jmp			= 0xE9;			// E9
			thunk.m_relproc		= (int)proc - ((int)this + sizeof(_MouseProcThunk));

#endif	// #if defined(_M_IX86)

			// write block from data cache and flush from instruction cache
			::FlushInstructionCache(::GetCurrentProcess(), &thunk, sizeof(thunk));
		}
	};
	
	public:
		/// c'tor
		CMouseProcHook();
		
		/// d'tor
		~CMouseProcHook();
		
	public:
		static DWORD OffsetOfLPARAM(void *pThis)
		{
			return (DWORD)( (char *)pThis + offsetof(CMouseProcHook, m_lParam) );
		}
		
		inline LPARAM GetLPARAM() const { return m_lParam; }
		inline HHOOK GetHHOOK() const { return m_hHook; }

		// Operations
	public:
		BOOL SetMouseProcHook(HINSTANCE hmod, DWORD dwThreadID);
		void UnhookMouseProcHook();
		BOOL IsMouseProcHooked() const { return (NULL != m_hHook); }
		
	private:
		MouseProcThunk m_thunk;
		HHOOK m_hHook;
		LPARAM m_lParam;
	};

public:
	static BOOL SetupMouseTracer(HINSTANCE hMod = NULL, DWORD dwThreadID = 0) { return CMouseTracerManagerImpl5::Instance().Setup(hMod, dwThreadID); }
	static void RemoveMouseTracer(DWORD dwThreadID = 0) { CMouseTracerManagerImpl5::Instance().Remove(dwThreadID); }
	
protected:
	static CMouseTracerManagerImpl5 &Instance() { static CMouseTracerManagerImpl5 instance; return instance; }
	
public:
	CMouseTracerManagerImpl5();
	virtual ~CMouseTracerManagerImpl5();
	
private:
	BOOL Setup(HINSTANCE hMod = NULL, DWORD dwThreadID = 0);
	void Remove(DWORD dwThreadID = 0);
	
private:
	static LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
	static CMouseTracer<CMouseTracerManager<CMouseTracerManagerImpl5> > *m_pTracer;
	static BOOL m_bTracerDeleted;

	static struct CMouseProcHookMap : std::map<DWORD, LPVOID>
	{
		// functor to delete the object from memory
		struct DeleteAndNull
		{
			// type determined automatically
			template<typename TPair>
			inline void operator() (TPair &tElement)
			{
				delete reinterpret_cast<CMouseTracerManagerImpl5::CMouseProcHook *>(tElement.second);
				tElement.second = NULL;
			}
		};
	} m_mouseProcHookMap;
};

#pragma warning(pop)

#endif // !defined(MOUSETRACERMANGERIMPL5_H_INCLUDED)
